#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <string.h>
#include <linux/if_arp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>

#define ETH_HW_ADDR_LEN 6
#define IP_ADDR_LEN 4
#define DEFAULT_DEVICE "eth0"

struct arp_packet {
	u_short hw_type;  
	u_short prot_type; 
	u_char  hw_addr_size; 
	u_char  prot_addr_size; 
	u_short op; 
	u_char sndr_hw_addr[ETH_HW_ADDR_LEN];
	u_char sndr_ip_addr[IP_ADDR_LEN];
	u_char rcpt_hw_addr[ETH_HW_ADDR_LEN];
	u_char rcpt_ip_addr[IP_ADDR_LEN];	
};

void fill_arp_packet(struct arp_packet* packet, struct sockaddr_ll addr_link, struct sockaddr_in addr_net, struct in_addr ip_dst) {
	memset(packet, '\0', sizeof(struct arp_packet));
	
	packet->hw_type = htons(ARPHRD_ETHER);
	packet->prot_type = htons(ETH_P_IP);
	packet->hw_addr_size = ETH_HW_ADDR_LEN;
	packet->prot_addr_size = IP_ADDR_LEN;
	packet->op = htons(1);
	memcpy(packet->sndr_hw_addr, addr_link.sll_addr, ETH_HW_ADDR_LEN);
	memcpy(packet->sndr_ip_addr, &addr_net.sin_addr, IP_ADDR_LEN);
	memcpy(packet->rcpt_ip_addr, &ip_dst, IP_ADDR_LEN);
	
}

int fill_sockaddr(struct sockaddr_ll *addr_ll, struct sockaddr_in *addr_in, 
					struct sockaddr_ll *destination, struct in_addr ip_dst) {
	memset(addr_ll, 0xFF, sizeof(struct sockaddr_ll));
	
	struct ifaddrs *ifa, *next_ifa;
	char *interface_name = NULL;
	getifaddrs(&ifa);
	next_ifa = ifa;
	struct in_addr addr, mask;
	while (next_ifa != NULL) {
		if (next_ifa->ifa_addr->sa_family == AF_INET) {
			addr = ((struct sockaddr_in*)next_ifa->ifa_addr)->sin_addr;
			mask = ((struct sockaddr_in*)next_ifa->ifa_netmask)->sin_addr;
			if ((addr.s_addr & mask.s_addr)==(ip_dst.s_addr & mask.s_addr)) {
				interface_name = next_ifa->ifa_name;
				*addr_in = *(struct sockaddr_in*)next_ifa->ifa_addr;
				break;
			}
		}
		next_ifa = next_ifa->ifa_next;
	}
	next_ifa = ifa;
	while (next_ifa != NULL) {
		if (strcmp (next_ifa->ifa_name, interface_name)==0 && (next_ifa->ifa_addr->sa_family==AF_PACKET)) {
			*addr_ll = *(struct sockaddr_ll*)next_ifa->ifa_addr;
			addr_ll->sll_protocol = htons(ETH_P_ARP);
			break;
		}
		next_ifa = next_ifa->ifa_next;
	}
	freeifaddrs(ifa);
	
	destination->sll_family = AF_PACKET;
	destination->sll_protocol = htons(ETH_P_ARP);
	destination->sll_ifindex = addr_ll->sll_ifindex;
	destination->sll_hatype = ARPHRD_ETHER;
	destination->sll_pkttype = PACKET_BROADCAST;
	destination->sll_halen = ETH_HW_ADDR_LEN;
	memset(destination->sll_addr, 0xFF, ETH_HW_ADDR_LEN);
}


int main(int argc, char** argv) {
	if (argc < 2) {
		printf("myArp: missing destination ip address operand\n");
		exit(-1);
	}
	struct in_addr ip_dst;
	if (!inet_aton(argv[1], &ip_dst)) {
		printf("First parameter should be an IP address");
		exit(-1);
	}
	
	int sock = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
	if (sock < 0) {
		perror("Cannot create a socket");
		return errno;
	}
	
	struct sockaddr_ll saddr_link, destination;
	struct sockaddr_in saddr_net;
	fill_sockaddr(&saddr_link, &saddr_net, &destination, ip_dst);
	if (bind(sock, (struct sockaddr*)&saddr_link, sizeof(struct sockaddr_ll))< 0) {
		perror("bind:");
		return errno;
	}
	
	struct arp_packet packet;
	fill_arp_packet(&packet, saddr_link, saddr_net, ip_dst);
	if (sendto(sock, &packet, sizeof(struct arp_packet), 0, (struct sockaddr*)&destination, sizeof(struct sockaddr_ll))<0) {
		perror("sendto");
		return errno;
	}
	
	struct arp_packet answer;
	if (recv(sock, &answer, sizeof(struct arp_packet), 0)<0) {
		perror("recv");
		return errno;
	}
	
	if (
	answer.op == htons(2) && 
	((struct in_addr*)answer.sndr_ip_addr)->s_addr == ip_dst.s_addr) {
		int i = 0;
		for (i = 0; i < ETH_HW_ADDR_LEN; ++i) printf("%02X:", answer.sndr_hw_addr[i]);
		printf("\n");
	} else {
		printf("Nobody answers\n");
	}
	
	close(sock);
	return 0;
} 
